#include "client.h"
#define MAX_FILENAME_LENGTH 64
#define MAX_FILE_CONTENT_SIZE 4096
#define MMAP_THRESHOLD (1024 * 1024 * 100) // 文件大小阈值，单位为字节

void printProgressBar(double percentage) 
{
    const int width = 50;
    int progress = width * percentage;

    printf("\033[2K"); // 清除当前行
    printf("\033[0G"); // 将光标移动到行首
    printf("Progress: [");

    for (int i = 0; i < width; ++i) {
        if (i < progress) {
            printf("\u2588"); // Unicode 实心方块
        } else {
            printf("\u2591"); // Unicode 灰色方块
        }
    }

    printf("] %.2f%%", percentage * 100);
    fflush(stdout);
}

int mmap_gets(off_t length, int wfd, int clientfd)
{
    //造一个文件空洞
    ftruncate(wfd, length);
    void* address = mmap(NULL, length, PROT_READ | PROT_WRITE, MAP_SHARED, wfd, 0);
    int ret = recv(clientfd, address, length, MSG_WAITALL);
    if(ret != length) {
        perror("recv mmap");
        munmap(address, length);
        close(wfd);
        return -1;
    }
    munmap(address, length);
    address = NULL;
    // 打印进度条
    double percentage = (double)ret / length;
    printProgressBar(percentage);

    //usleep(1000);
    printf("\nmmap File received successfully!\n");

    return 0;
}

int normal_gets(off_t length, int clientfd, char* file_content, int wfd)
{
    // 接收文件内容
    off_t receivedLength = 0;
    while (receivedLength < length) {
        int bytes_to_receive = (length - receivedLength) < MAX_FILE_CONTENT_SIZE ? (length - receivedLength) : MAX_FILE_CONTENT_SIZE;
        int ret = recvn(clientfd, file_content, bytes_to_receive);
        if (ret < 0) {
            perror("recv");
            close(wfd);
            return -1;
        } else if (ret == 0) {
            // 连接关闭
            close(wfd);
            return -1;
        }
        receivedLength += ret;

        // 打印进度条
        double percentage = (double)receivedLength / length;
        printProgressBar(percentage);

        usleep(1);
        // 写入文件
        ssize_t bytes_written = write(wfd, file_content, ret);
        if (bytes_written < 0) {
            perror("write");
            close(wfd);
            return -1;
        } else if (bytes_written != ret) {
            // 写入不完整
            fprintf(stderr, "Incomplete write\n");
            close(wfd);
            return -1;
        }
    }
    printf("\nFile received successfully!\n");

    return 0;
}

int getsFile(int sockfd) 
{
    int clientfd = sockfd;
    char filename_buffer[MAX_FILENAME_LENGTH + 1]; 
    char file_content[MAX_FILE_CONTENT_SIZE];

    // 1. 接收文件长度和名字
    int len = 0;
    int ret = recv(clientfd, &len, sizeof(len), 0);
    if (ret <= 0) {
        perror("recv filename length");
        return -1;
    }

    printf("filename length: %d\n", len);

    // 1.1 检查文件名长度是否合法
    if (len > MAX_FILENAME_LENGTH) {
        printf("File not found on server.\n");
        return -1;
    }

    // 1.2 定义文件名缓冲区
    ret = recvn(clientfd, filename_buffer, len);
    if (ret <= 0) {
        perror("recv filename");
        return -1;
    }

    filename_buffer[len] = '\0'; // 添加字符串结尾
    printf("filename: %s\n", filename_buffer);

    // 1.3接收文件大小
    off_t downloadfile_size = 0;
    ret = recvn(clientfd, &downloadfile_size, sizeof(downloadfile_size));
    if(ret < 0) {
        perror("recv");
        return -1;
    }
    printf("downloadfile_size: %ld\n", downloadfile_size);

    // 2.断点续传，判断文件是否存在，如果不存在就创建, 如果已存在就打开
    struct stat client_file_info;
    ret = stat(filename_buffer, &client_file_info);
    // 3.文件存在，向服务器发送本地文件大小信息
    if (ret == 0) {
        off_t localFileSize = client_file_info.st_size;
        printf("Local file %ld exist and size: %ld\n", client_file_info.st_ino, localFileSize);
        //sendn(clientfd, &localFileSize, sizeof(localFileSize));
        // 3.1如果本地文件且大小等于要接收的大小，则不需要断点续传
        if (localFileSize == downloadfile_size) {
            printf("Local file exit and do not continue gets.\n");
            // 3.2如果本地文件存在且小于要接收的文件大小，需要进行断点续传
        }else if(localFileSize >= 0 && localFileSize < downloadfile_size) {
            // 3.3向服务器发送此时本地文件的大小
            sendn(clientfd, &localFileSize, sizeof(localFileSize));
            // 打开文件描述符
            int wfd = open(filename_buffer, O_RDWR, 0644);
            if (wfd == -1) {
                perror("open");
                return -1;
            }
            // 3.3 再接收文件内容长度
            off_t length = 0;
            ret = recvn(clientfd, &length, sizeof(length));
            if (ret <= 0) {
                perror("recv file content length");
                close(wfd);
                return -1;
            }
            printf("need to resuming download file content length: %ld\n", length);
            // 3.4跳转到本地文件的末尾
            off_t offset = lseek(wfd, localFileSize, SEEK_SET);
            if (offset == -1) {
                perror("lseek");
                close(wfd);
                return -1;
            }
            printf("offset: %ld\n", offset);
            // 3.5判断使用mmap接收还是normal接收
            if(offset < length && length - offset > MMAP_THRESHOLD) {
                ret = mmap_gets(length, wfd, clientfd);
                if(ret == -1) {
                    perror("mmap_gets");
                    close(wfd);
                    return -1;
                }
            }else {
                normal_gets(length, clientfd, file_content, wfd);
            }

            // 3.6关闭文件描述符
            close(wfd);
        } 
    }else if(ret == -1) {
        // 4.文件不存在，向服务器发送-1作为本地文件大小信息
        off_t localFileSize = -1;
        printf("Local file does not exist.\n");
        sendn(clientfd, &localFileSize, sizeof(localFileSize));
        // 4.1 创建文件
        int wfd = open(filename_buffer, O_CREAT | O_RDWR, 0644);
        if (wfd == -1) {
            perror("open");
            close(wfd);
            return -1;
        }
        // 4.2 再接收文件内容长度
        off_t length = 0;
        ret = recvn(clientfd, &length, sizeof(length));
        if (ret <= 0) {
            perror("recv file content length");
            close(wfd);
            return -1;
        }
        printf("file content length: %ld\n", length);
        // 4.3判断使用mmap接收还是normal
        if(length > MMAP_THRESHOLD) {
            int ret = mmap_gets(length, wfd, clientfd);
            if(ret == -1) {
                perror("mmap_gets");
                close(wfd);
                return -1;
            }
        }else {
            normal_gets(length, clientfd, file_content, wfd);
        }

        // 4.4关闭文件描述符
        close(wfd);
    }

    return clientfd;
}

